xml格式介绍、resoures.arsc结构介绍以及Android 资源相关需求

Android 资源文件关系到了很多方面:

  1. 屏幕适配。
  2. 插件化(多套皮肤需求)。
  3. 包优化(从资源方面入手)。

所以了解Android 中资源存储的机制对相关的需求是有帮助的。

主要从三个方面来介绍下,资源相关基础、xml 格式介绍、resoures.arsc结构介绍。

资源相关基础

主要有两点:资源分类以及资源配置信息。可以从官网Providing Resources找到相关信息。然后就是资源文件打包的介绍。

资源分类以及配置信息

资源分类:animator、anim、color、drawable、mipmap、layout、menu、raw、values、xml。

资源配置信息(可能会不断的增加新的配置,可以从官网获取最新的Providing Resources):

配置 限定符值
MCC 和 MNC 示例:
mcc310、mcc310-mnc004、mcc208-mnc00等等
语言和区域 示例:en、fr、en-rUS、fr-rFR、fr-rCA等等
布局方向 ldrtl、ldltr
smallestWidth sw< N >dp
示例:sw320dp、sw600dp、sw720dp等等
可用宽度 w< N >dp
示例:w720dp、w1024dp等等
可用高度 h< N >dp
示例:h720dp、h1024dp等等
屏幕尺寸 small
normal、large、xlarge
屏幕纵横比 long、notlong
圆形屏幕 round、notround
屏幕方向 port、land
UI 模式 car、desk、television、appliance watch
夜间模式 night、notnight
屏幕像素密度 (dpi) ldpi、mdpi、hdpi、xhdpi、xxhdpi
xxxhdpi、nodpi、tvdpi、anydpi
触摸屏类型 notouch、finger
键盘可用性 keysexposed、keyshidden、keyssoft
主要文本输入法 nokeys、qwerty、12key
导航键可用性 navexposed、navhidden
主要非触摸导航方法 nonav、dpad、trackball、wheel
平台版本(API 级别) 示例:v3、v4、v7等等

资源文件打包

Android res 中的xml 文件(不包括assets 文件夹里面的xml)以及AndroidManifest.xml 在打包的时候都会被转化成二进制的xml。

这些二进制格式的XML文件分别有一个字符串资源池,用来保存文件中引用到的每一个字符串,包括XML元素标签、属性名称、属性值,以及其它的一切文本值所使用到的字符串。这样原来在文本格式的XML文件中的每一个放置字符串的地方在二进制格式的XML文件中都被替换成一个索引到字符串资源池的整数值。这样做有两个好处:
A. 文件占用更小。例如,假设在原来的文本格式的XML文件中,有四个地方使用的都是同一个字符串,那么在最终编译出来的二进制格式的XML文件中,字符串资源池只有一份字符串值,而引用它的四个地方只占用一个整数值。
B. 解析速度更快。由于在二进制格式的XML文件中,所有的XML元素标签和属性等值都是使用整数来描述的,因此,在解析的过程中,就不再需要进行字符串解析,这样就可以提高解析速度。(来自老罗博客)

其实有一个问题:为什么Android 要分那么多种类的资源,这样不是非常的复杂吗?
因为市面上Android 手机种类很多(不同的价格、文化、语言等适用于不同的人)。为了让软件更加符合一款手机使用就必须适配不同的方案,因此就有很多不同种类的资源。

Android 二进制xml 格式介绍

AndroidManifest 二进制文件格式
(上面AndroidManifest 二进制文件格式图片是网上盗的图)

1.String Chunk:解析出字符串的信息,包含所有的字符串。
比如下面的一个Activity 的申明。

1
2
3
4
5
6
<activity android:configChanges="keyboardHidden|orientation|screenLayout|screenSize" android:label="@string/app_name" android:name="egos.samplez.lifestyle.SampleTextActivity" android:theme="@style/SampleAppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

那么String Chunk 中就会把activity、android、configChanges、label、name 等一系列的String 都保存在String Chunk 中(只需要保存一次,这样就减少了存储空间了)。

2.ResourceId Chunk:保存的是AndroidManifest 中用到的系统属性值对应的资源Id。
比如对应的是:0101021b、0101021c、0101020c,解析出来对应的就是android.R中的versionCode、versionName、minSdkVersion。

3.Start Namespace Chunk:这个跟xml 格式相关。Android 中xml 采用的是Schema 格式,所以这里主要是解析Prefix 和Uri。
比如下图就是一个StartNamespace。

1
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="egos.sample1" platformBuildVersionCode="24" platformBuildVersionName="7.0">

解析的时候就会得出Prefix 为android,而Uri 为http://schemas.android.com/apk/res/android。

4.StartTag Chunk:这里开始就是xml 中对应的信息了。
下面是一个xml 中相关的信息(是两个StartTag)。

1
2
3
4
5
6
7
8
9
<manifest xmls:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="egos.sample1"
platformBuildVersionCode="25"
platformBuildVersionName="7.1.1">
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="25">

会解析两个StartTag Chunk 得到相应的信息(里面所有的字符串信息都是保存在String Chunk 中)。每一个属性值(比如上面的minSdkVersion、targetSdkVersion等)会被解析成[Namespace,Uri,Name,ValueString,Data],包含5个值,每个值对应4字节。

5.EndTag Chunk:与StartTag Chunk 相对应。
上面的StartTag Chunk 的代码会与下面相对应。

1
2
</uses-sdk>
</manifest>

6.End Namespace Chunk:与Start Namespace Chunk 相对应。
上面的Start Namespace Chunk 会与下面的内容相对应。

1
</manifest>

AndroidManifest.xml 文件格式可以总结一下:

AndroidManifest 文件格式总结

验证了最上面关于二进制xml 文件的总结(老罗的总结):将所有的字符串提取出来,减少了重复字符串的存储,压缩了xml 大小。解析的时候(从StartTagChunk 开始)不在读取字符串,而是直接读取整数,提高解析速度。

res 中的xml 经过编译以后的格式和AndroidManifest.xml 大体是一致的,主要的不同是StringChunk 的格式有一些区别。所以这里也可以看出一些资源优化的点,减少xml 中string,尽量把所有的string 字符串写在string.xml 中。(这里之前跟朋友争论说在xml 写xxdp 也会增大xml 大小,其实是不会的,因为都是占4字节。)

resoures.arsc 文件结构介绍

resoures.arsc 文件(下面简称arsc)中存储的是public.xml(public.xml 文件可以通过apktool 反编译得到,在/res/values 目录下)中所有的内容。内容比较的多和复杂,比如一张图片ic_launcher.png 在多个dpi 中都要存储,一个string(比如:app_name)在不同的语言(比如:values-en、values-fr)中不一样。这些不同的资源配置信息也需要在arsc 中存储起来。(其实打包以后/res/values 目录下面的所有的信息都是保存在arsc 中了,可以通过解压apk 验证,不存在任何values 文件夹。)后续通过id 加载layout,查找drawable、color、string等都跟arsc 中存储的内容相关。

先给出一张网上的神图。
resoures.arsc 文件结构
(上面resoures.arsc 文件结构的图片是网上盗的图,上图有一些错误,最下面的Type Spec 和Config List 应该是交替出现的)

主要包含了3个资源池:

  1. string 资源池:所有定义的string 信息和资源的路径信息。
  2. type 资源池:所有定义的资源type。例如:attr、drawable、layout、anim等
  3. key 资源池:所有的定义在各种资源中的name(上面已经有介绍,在public.xml 中存在的name 都会保存在里面)。

string 资源池分成了两部分,string 部分和style 部分(字符串是可以设置style)。
string 部分包含了xml 中定义的string(比如string.xml 中的<string name="app_name">App4</string>定义的字符串App)、资源的路径(比如res/drawable-xxhdpi-v11/leak_canary_notification.png)。
style 部分则是存储了string.xml对应string 的样式。如下:

1
2
<string name="large_text"><b>Material</b> is the <i>metaphor</i></string> //0~7 b,16~23 i
<string name="large_text2"><b>Bold</b>, graphic, <i>intentional</i>.</string> //0~3 b,15~25 i

下面给出一些string 资源池里面的string 更加好理解。

1
2
3
4
5
6
7
App4
res/drawable/abc_btn_check_material.xml
res/color/abc_primary_text_disable_only_material_light.xml
res/layout/design_navigation_menu_item.xml
res/menu/menu_normal_app_bar_layout.xml
res/raw/video1.mp4
...

字符串资源池解析以后就是存储的资源信息了。
下面就直接介绍后面的存储格式了(Android逆向之旅—解析编译之后的Resource.arsc文件格式更加的详细)。

1.接下来的格式就是直接解析一种资源(对应的是图中的RES_TABLE_TYPE_SPEC_TYPE 中的type id),假设对应的是mipmap 资源。并且会解析出该资源会有哪些不同类型的资源配置(对应的是图中的RES_TABLE_TYPE_SPEC_TYPE 中的资源项组spec 数)。

2.接下来就是mipmap 资源的配置信息(上面第1步假设的mipmap,对应的是图中的RES_TABLE_TYPE_TYPE,会解析出第1步中资源项组spec 数种不同配置信息),解析出配置信息(运营商、locale、屏幕尺寸等等)。然后就会解析出这种配置下所有mipmap 的信息。

3.解析出mipmap 的信息(对应的是图中的ResTable_entry),包括资源对应的名称(比如:ic_launcher),对应的值信息(比如:res/mipmap-mdpi-v4/ic_launcher.png)等等。

最后就是重复的解析第2步给出的第3步数量的ResTable_entry),然后解析第1步给出的第2步数量RES_TABLE_TYPE_TYPE。

所以arsc 的格式可以简单的重新画一个,如下:
arsc 格式总结

所以对于资源的查找应该也有一个比较清晰的认识。通过资源id,取前两位先拿到对应的packageId,取3、4位拿到typeId,这样就定位到package 和type。最后就根据当前设备的维度信息,寻找到最合适的资源。最后如果得到的是资源文件(layout、drawable等)再去解析就可以了。

相关需求

最开始已经有提到过,还可以加一些。包优化AndResGuard。皮肤包ThemeSwitch

总结

这篇博客在很早之前就写了大部分,主要是在研究项目中关于皮肤代码时的总结(好记性不如烂笔头,所以这次记下来了),并且不是自己完整的研究了arsc 的生成或者xml 的解析,主要是在老罗的博客中边看边学,在尼古拉斯_赵四的博客中慢慢验证的。所以可能是会有错误的,如果你发现了错误,方便的话可以发一份邮件给我(相信你找得到)。
然后Android 中资源相关的内容,个人觉得是非常重要的,熟悉一下还是有很大帮助的。

Providing Resources
Android资源管理框架(Asset Manager)简要介绍和学习计划
Android逆向之旅—解析编译之后的AndroidManifest文件格式
Android逆向之旅—解析编译之后的Resource.arsc文件格式